﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Windows.Input;
using System.Windows.Media;
using Elca.MvvmHelpers;

namespace ULSDeobfuscator {

    public class EditColorRuleViewModel : BaseViewModel {

        public ICommand OKCommand { get; private set; }
        public ICommand CancelCommand { get; private set; }
        public ICommand LoadCommand { get; private set; }

        public ColorRule Rule { get; set; }

        public string Title { get; set; }

        public List<FilterChoice> ProcessesList { get; private set; }
        public List<FilterChoice> AreasList { get; private set; }
        public List<FilterChoice> CategoriesList { get; private set; }
        public List<FilterChoice> LevelsList { get; private set; }
        public List<FilterChoice> EventsList { get; private set; }

        public double WindowHeight {
            get { return App.GlobalSettings.EditColorRuleWindowHeight; }
            set {
                if (App.GlobalSettings.EditColorRuleWindowHeight != value) {
                    App.GlobalSettings.EditColorRuleWindowHeight = value;
                    OnPropertyChanged("WindowHeight");
                }
            }
        }

        public double WindowWidth {
            get { return App.GlobalSettings.EditColorRuleWindowWidth; }
            set {
                if (App.GlobalSettings.EditColorRuleWindowWidth != value) {
                    App.GlobalSettings.EditColorRuleWindowWidth = value;
                    OnPropertyChanged("WindowWidth");
                }
            }
        }

        public double WindowLeft {
            get { return App.GlobalSettings.EditColorRuleWindowLeft; }
            set {
                if (App.GlobalSettings.EditColorRuleWindowLeft != value) {
                    App.GlobalSettings.EditColorRuleWindowLeft = value;
                    OnPropertyChanged("WindowLeft");
                }
            }
        }

        public double WindowTop {
            get { return App.GlobalSettings.EditColorRuleWindowTop; }
            set {
                if (App.GlobalSettings.EditColorRuleWindowTop != value) {
                    App.GlobalSettings.EditColorRuleWindowTop = value;
                    OnPropertyChanged("WindowTop");
                }
            }
        }

        private bool _isForegroundColorManaged;
        public bool IsForegroundColorManaged {
            get { return _isForegroundColorManaged; }
            set {
                if (_isForegroundColorManaged != value) {
                    _isForegroundColorManaged = value;
                    OnPropertyChanged("IsForegroundColorManaged");
                    OnPropertyChanged("ResultingForegroundColor");
                }
            }
        }

        private Color _foregroundColor;
        public Color ForegroundColor {
            get { return _foregroundColor; }
            set {
                if (_foregroundColor != value) {
                    _foregroundColor = value;
                    IsForegroundColorManaged = true;
                    CommandManager.InvalidateRequerySuggested();
                    OnPropertyChanged("ForegroundColor");
                    OnPropertyChanged("ResultingForegroundColor");
                }
            }
        }

        private bool _isBackgroundColorManaged;
        public bool IsBackgroundColorManaged {
            get { return _isBackgroundColorManaged; }
            set {
                if (_isBackgroundColorManaged != value) {
                    _isBackgroundColorManaged = value;
                    OnPropertyChanged("IsBackgroundColorManaged");
                    OnPropertyChanged("ResultingBackgroundColor");
                }
            }
        }

        private Color _backgroundColor;
        public Color BackgroundColor {
            get { return _backgroundColor; }
            set {
                if (_backgroundColor != value) {
                    _backgroundColor = value;
                    IsBackgroundColorManaged = true;
                    CommandManager.InvalidateRequerySuggested();
                    OnPropertyChanged("BackgroundColor");
                    OnPropertyChanged("ResultingBackgroundColor");
                }
            }
        }

        /// <summary>
        /// The default color, except if the user chose to override it
        /// </summary>
        public Color ResultingForegroundColor {
            get {
                if (IsForegroundColorManaged) {
                    return ForegroundColor;
                }

                return DefaultForegroundColor;
            }
        }
        public Color DefaultForegroundColor { get; set; }

        public Color ResultingBackgroundColor {
            get {
                if (IsBackgroundColorManaged) {
                    return BackgroundColor;
                }

                return DefaultBackgroundColor;
            }
        }
        public Color DefaultBackgroundColor { get; set; }

        /// <summary>
        /// the width of an items control showing a list of <c>FilterChoice</c>
        /// </summary>
        private double _itemsControlWidth;
        public double ItemsControlWidth {
            get { return _itemsControlWidth; }
            set {
                if (_itemsControlWidth != value) {
                    _itemsControlWidth = value;
                    OnPropertyChanged("ItemsControlWidth");
                }
            }
        }

        private ObservableCollection<double> _processItemsWidth;
        public ObservableCollection<double> ProcessItemsWidth {
            get { return _processItemsWidth; }
            set {
                if (_processItemsWidth != value) {
                    _processItemsWidth = value;
                    OnPropertyChanged("ProcessItemsWidth");
                }
            }
        }

        private bool _areProcessItemsWidthReady;
        public bool AreProcessItemsWidthReady {
            get { return _areProcessItemsWidthReady; }
            set {
                if (_areProcessItemsWidthReady != value) {
                    _areProcessItemsWidthReady = value;
                    if (_areProcessItemsWidthReady) {
                        RecomputeProcessItemsWidth();
                    }
                    OnPropertyChanged("AreProcessItemsWidthReady");
                }
            }
        }

        private ObservableCollection<double> _categoryItemsWidth;
        public ObservableCollection<double> CategoryItemsWidth {
            get { return _categoryItemsWidth; }
            set {
                if (_categoryItemsWidth != value) {
                    _categoryItemsWidth = value;
                    OnPropertyChanged("CategoryItemsWidth");
                }
            }
        }

        private bool _areCategoryItemsWidthReady;
        public bool AreCategoryItemsWidthReady {
            get { return _areCategoryItemsWidthReady; }
            set {
                if (_areCategoryItemsWidthReady != value) {
                    _areCategoryItemsWidthReady = value;
                    if (_areCategoryItemsWidthReady) {
                        RecomputeCategoryItemsWidth();
                    }
                    OnPropertyChanged("AreCategoryItemsWidthReady");
                }
            }
        }

        private ObservableCollection<double> _areaItemsWidth;
        public ObservableCollection<double> AreaItemsWidth {
            get { return _areaItemsWidth; }
            set {
                if (_areaItemsWidth != value) {
                    _areaItemsWidth = value;
                    OnPropertyChanged("AreaItemsWidth");
                }
            }
        }

        private bool _areAreaItemsWidthReady;
        public bool AreAreaItemsWidthReady {
            get { return _areAreaItemsWidthReady; }
            set {
                if (_areAreaItemsWidthReady != value) {
                    _areAreaItemsWidthReady = value;
                    if (_areAreaItemsWidthReady) {
                        RecomputeAreaItemsWidth();
                    }
                    OnPropertyChanged("AreAreaItemsWidthReady");
                }
            }
        }

        private ObservableCollection<double> _levelItemsWidth;
        public ObservableCollection<double> LevelItemsWidth {
            get { return _levelItemsWidth; }
            set {
                if (_levelItemsWidth != value) {
                    _levelItemsWidth = value;
                    OnPropertyChanged("LevelItemsWidth");
                }
            }
        }

        private bool _areLevelItemsWidthReady;
        public bool AreLevelItemsWidthReady {
            get { return _areLevelItemsWidthReady; }
            set {
                if (_areLevelItemsWidthReady != value) {
                    _areLevelItemsWidthReady = value;
                    if (_areLevelItemsWidthReady) {
                        RecomputeLevelItemsWidth();
                    }
                    OnPropertyChanged("AreLevelItemsWidthReady");
                }
            }
        }

        private ObservableCollection<double> _eventIDItemsWidth;
        public ObservableCollection<double> EventIDItemsWidth {
            get { return _eventIDItemsWidth; }
            set {
                if (_eventIDItemsWidth != value) {
                    _eventIDItemsWidth = value;
                    OnPropertyChanged("EventIDItemsWidth");
                }
            }
        }

        private bool _areEventIDItemsWidthReady;
        public bool AreEventIDItemsWidthReady {
            get { return _areEventIDItemsWidthReady; }
            set {
                if (_areEventIDItemsWidthReady != value) {
                    _areEventIDItemsWidthReady = value;
                    if (_areEventIDItemsWidthReady) {
                        RecomputeEventIDItemsWidth();
                    }
                    OnPropertyChanged("AreEventIDItemsWidthReady");
                }
            }
        }

        public EditColorRuleViewModel() {

            // OK is foreground or background is managed and at least one condition is present
            OKCommand = new ConditionalCommand(OnOK, () => (IsBackgroundColorManaged || IsForegroundColorManaged) &&
                                                           (ProcessesList.Count(fc => fc.IsSelected) > 0 || AreasList.Count(fc => fc.IsSelected) > 0 ||
                                                            CategoriesList.Count(fc => fc.IsSelected) > 0 || LevelsList.Count(fc => fc.IsSelected) > 0 ||
                                                            EventsList.Count(fc => fc.IsSelected) > 0 || !String.IsNullOrEmpty(Rule.Message) ||
                                                            !String.IsNullOrEmpty(Rule.Correlation) || Rule.IsMultiLine || Rule.HasAnnotation));
            CancelCommand = new ExecutableCommand(OnCancel);
            LoadCommand = new ExecutableCommand(OnLoad);

            ProcessItemsWidth = new ObservableCollection<double>();
            AreaItemsWidth = new ObservableCollection<double>();
            LevelItemsWidth = new ObservableCollection<double>();
            CategoryItemsWidth = new ObservableCollection<double>();
            EventIDItemsWidth = new ObservableCollection<double>();
        }

        private void OnClosing() {

        }
        private void OnOK() {
            CopyDataToRule();
            OnClosing();
            RaiseCloseRequest(true);
        }

        private void CopyDataToRule() {
            
            Rule.Processes.Clear();
            Rule.Processes.AddRange(ProcessesList.Where(fc => fc.IsSelected).Select(fc => fc.ItemName));
            Rule.Areas.Clear();
            Rule.Areas.AddRange(AreasList.Where(fc => fc.IsSelected).Select(fc => fc.ItemName));
            Rule.Categories.Clear();
            Rule.Categories.AddRange(CategoriesList.Where(fc => fc.IsSelected).Select(fc => fc.ItemName));
            Rule.EventIDs.Clear();
            Rule.EventIDs.AddRange(EventsList.Where(fc => fc.IsSelected).Select(fc => fc.ItemName));
            Rule.Levels.Clear();
            Rule.Levels.AddRange(LevelsList.Where(fc => fc.IsSelected).Select(fc => fc.ItemName));

            if (IsBackgroundColorManaged) {
                Rule.BackgroundColor = BackgroundColor;
            } else {
                Rule.BackgroundColor = null;
            }

            if (IsForegroundColorManaged) {
                Rule.ForegroundColor = ForegroundColor;
            } else {
                Rule.ForegroundColor = null;
            }
        }

        private void OnCancel() {
            OnClosing();
            RaiseCloseRequest(false);
        }

        private void OnLoad() {
            
            if (Rule.ForegroundColor != null) {
                IsForegroundColorManaged = true;
                ForegroundColor = Rule.ForegroundColor.Value;
            }
            if (Rule.BackgroundColor != null) {
                IsBackgroundColorManaged = true;
                BackgroundColor = Rule.BackgroundColor.Value;
            }
        }

        private void RecomputeProcessItemsWidth() {

            double maxItemWdith = _processItemsWidth.Max();
            for (int i = 0; i < _processItemsWidth.Count; i++) {
                _processItemsWidth[i] = maxItemWdith;
            }
        }

        private void RecomputeAreaItemsWidth() {

            double maxItemWdith = _areaItemsWidth.Max();
            for (int i = 0; i < _areaItemsWidth.Count; i++) {
                _areaItemsWidth[i] = maxItemWdith;
            }
        }

        private void RecomputeCategoryItemsWidth() {

            double maxItemWdith = _categoryItemsWidth.Max();
            for (int i = 0; i < _categoryItemsWidth.Count; i++) {
                _categoryItemsWidth[i] = maxItemWdith;
            }
        }

        private void RecomputeLevelItemsWidth() {

            double maxItemWdith = _levelItemsWidth.Max();
            for (int i = 0; i < _levelItemsWidth.Count; i++) {
                _levelItemsWidth[i] = maxItemWdith;
            }
        }

        private void RecomputeEventIDItemsWidth() {

            double maxItemWdith = _eventIDItemsWidth.Max();
            for (int i = 0; i < _eventIDItemsWidth.Count; i++) {
                _eventIDItemsWidth[i] = maxItemWdith;
            }
        }

        private static void SetRuleElementsList(IList<FilterChoice> availableChoices, IEnumerable<string> existingChoices,
                                                out List<FilterChoice> destinationChoices) {
            
            // copy the list we received
            destinationChoices = availableChoices.Select(fc => new FilterChoice { ItemName = fc.ItemName, IsSelected = false }).ToList();

            // add items from the rule in case they are not already there
            // and select those in the rule
            foreach (var element in existingChoices) {

                var existingElement = destinationChoices.FirstOrDefault(fc => fc.ItemName == element);
                if (existingElement != null) {
                    existingElement.IsSelected = true;
                } else {
                    destinationChoices.Add(new FilterChoice {
                        ItemName = element,
                    });
                }
            }
        }

        public void SetProcessesList(IList<FilterChoice> processesList) {
            List<FilterChoice> filters;
            SetRuleElementsList(processesList, Rule.Processes, out filters);
            ProcessesList = filters;
        }

        public void SetAreasList(IList<FilterChoice> areasList) {
            List<FilterChoice> filters;
            SetRuleElementsList(areasList, Rule.Areas, out filters);
            AreasList = filters;
        }

        public void SetCategoriesList(IList<FilterChoice> categoriesList) {
            List<FilterChoice> filters;
            SetRuleElementsList(categoriesList, Rule.Categories, out filters);
            CategoriesList = filters;
        }

        public void SetLevelsList(IList<FilterChoice> levelsList) {
            List<FilterChoice> filters;
            SetRuleElementsList(levelsList, Rule.Levels, out filters);
            LevelsList = filters;
        }

        public void SetEventIDsList(IList<FilterChoice> eventIDsList) {
            List<FilterChoice> filters;
            SetRuleElementsList(eventIDsList, Rule.EventIDs, out filters);
            EventsList = filters;
        }

    }
}
